<?php
/**
 * 游戏处理
 *
 * 游戏准备
 * 开始游戏
 * 重新开始游戏
 * code说明
 * 0=>失败 201=>重新链接 200=>成功
 * 2001 => 下家已梭哈自动过牌
 * 2002 => 全部叫牌发牌
 * 2003 => 结算
 */

namespace app\handle;

use app\common\utils\RedisUtils;
use app\handle\Table;
use app\chess\NewPoker;
use app\common\model\UserModel;
use app\common\model\UserGradeModel;

Class Game
{
    /**
     * 桌子处理
     *
     * @var [type]
     */
    public $table;

    /**
     * 桌子id
     *
     * @var [type]
     */
    private $tid;

    /**
     * 用户id
     *
     * @var [type]
     */
    private $uid;

    /**
     * 默认倒计时
     *
     * @var [type]
     */
    private $defaultTime;

    /**
     * game key
     *
     * @var [type]
     */
    private $gkey;

    public function __construct($tid, $uid)
    {
        $this->table = new Table($tid, $uid);
        $this->tid = $tid;
        $this->uid = $uid;
        $this->defaultTime = 20;
        $this->gkey = [
            /**
             * 牌堆
             * key = tid
             * val = array 一维
             */
            'cards' => 'table_cards_'.$tid,

            /**
             * 手牌
             * key = tid.uid
             * val = array 二维
             */
            'hands' => 'table_hands_'.$tid,

            /**
             * 叫牌流程
             * key = tid
             * val = array 三维
             */
            'flow'  => 'table_flow_'.$tid,

            /**
             * 下家操作
             * key = tid
             * val = array 一维
             */
            'oper'  => 'table_oper_'.$tid,

            /**
             * 加注倍数
             * key = tid
             * val = array 一维
             */
            'fill'  => 'table_fill_'.$tid,

            /**
             * 当前说话的位置
             * key = tid
             * val = number
             */
            'speakpos' => 'table_speak_pos_'.$tid,

            /**
             * 设置当前投注金额
             * key = tid
             * val = number
             */
            'bet' => 'table_bet_'.$tid,

            /**
             * 当前投注的倍数
             * key = tid
             * val = number
             */
            'multiple' => 'table_multiple_'.$tid,

            /**
             * 结算当前局状态
             */
            'settlement_status' => 'table_settlement_status'.$tid
        ];
    }

    /**
     * 获取游戏基本信息
     *
     * @return [type] [description]
     */
    public function gameStartInfo()
    {
        return [
            'hands' => $this->getTablePosHands(),
            'flows' => $this->getSpeakFlows(),
            'pos'   => $this->table->getPlayerPos($this->uid)
        ];
    }

    /**
     * 获取结算状态
     *
     * @return [type] [description]
     */
    public function getSettlementStatus()
    {
        $status = RedisUtils::get($this->gkey['settlement_status']);
        if (empty($status)) {
            return false;
        }
        return $status;
    }

    /**
     * 设置结算状态
     *
     * @param [type] $status [description]
     */
    public function setSettlementStatus($status)
    {
        RedisUtils::set($this->gkey['settlement_status'], $status);
    }

    /**
     * 结算
     *
     * @return [type] [description]
     */
    public function settlement()
    {
        $result = $this->table->result;
        if ($this->getSettlementStatus()) {
            $result['msg'] = '结算中';
            return $result;
        }
        $this->setSettlementStatus(true);

        if (!$this->isSettlement()) {
            $result['msg'] = '尚未到结算状态';
            return $result;
        }

        $pos = $this->table->getTablePos();
        $hands = $this->getTablePosHands();
        $flows = $this->getSpeakFlows();
        $ugModel = new UserGradeModel;
        $innings = $this->table->getTableCurInnings();

        $disCards = []; // 弃牌玩家
        $notDisCards = []; // 未弃牌玩家
        foreach ($flows as $k => $v) {
            $uid = $this->table->getUidByPos($k);
            $bet = $this->table->getUserinfo($uid)['bet'];
            $this->openCardByUid($pos[$k]); // 打开手牌

            if ($v['isDisCard']) {
                $addData = [];
                $tmp = [
                    'tid' => $this->tid,
                    'uid' => $uid,
                    'innings' => $innings,
                    'win' => 0-$bet
                ];
                array_push($addData, $tmp);
                $ugModel->insertGrade($addData);
                $disCards[$k] = $v;
            } else {
                $notDisCards[$k] = $v;
            }
        }

        // 按牌面大小牌讯
        $sizeSmallCards = $this->sizeToSmallCardsSort($notDisCards);

        // 牌面大到小排序：需结算
        $count = 0;
        $total = $this->table->getTotalBet();
        foreach ($sizeSmallCards as $k1 => $v1) {
            $count++;
            foreach ($notDisCards as $k2 => $v2) {
                $uid = $this->table->getUidByPos($k2);
                $uinfo = $this->table->getUserinfo($uid);

                // 最大
                if ($count === 1) {
                    if ($v1 == $v2) {
                        if ($uinfo['balance'] > 0) {
                            // 全部赢走
                            $this->winSettlement($uinfo['bet'], $total, $uid);
                            $total = 0;
                        } else {
                            // 全部赢走
                            if ($uinfo['bet'] >= $total/count($pos)) {
                                $this->winSettlement($uinfo['bet'], $total, $uid);
                                $total = 0;
                            } else {
                                // 只能赢走部分
                                $winTotal = $uinfo['bet']*count($pos);
                                $this->winSettlement($uinfo['bet'], $winTotal, $uid);
                                $total = $total - $winTotal;
                            }
                        }
                        unset($notDisCards[$k2]);
                    }
                }

                // 后面的大小
                if ($count === 2 && $v1 == $v2 && $total > 0 ||
                    $count === 3 && $v1 == $v2 && $total > 0 ||
                    $count === 4 && $v1 == $v2 && $total > 0 ||
                    $count === 5 && $v1 == $v2 && $total > 0) {
                    // 全部退回
                    if ($uinfo['bet'] >= $total/count($pos)) {
                        $this->baceSettlement($uinfo['bet'], $total, $uid);
                        $total = 0;
                    } else {
                        // 只能退回部分
                        $backTotal = $uinfo['bet']*count($pos);
                        $this->baceSettlement($uinfo['bet'], $backTotal, $uid);
                        $total = $total - $backTotal;
                    }
                    unset($notDisCards[$k2]);
                }
            }
        }

        if (!empty($notDisCards)) {
            $this->loseSettlement($notDisCards);
        }
        return [
            'code' => 200,
            'msg'  => '结算成功'
        ];
    }

    /**
     * 输结算
     *
     * @return [type] [description]
     */
    public function loseSettlement($list)
    {
        $ugModel = new UserGradeModel;
        $innings = $this->table->getTableCurInnings();
        $listData = [];
        foreach ($list as $k => $v) {
            $uid = $this->table->getUidByPos($k);
            $bet = $this->table->getUserinfo($uid)['bet'];

            $tmp = [
                'tid' => $this->tid,
                'uid' => $uid,
                'innings' => $innings,
                'win' => 0-$bet
            ];
            array_push($listData, $tmp);
        }
        $ugModel->insertGrade($listData);
    }

    /**
     * 退回金额
     *
     * @return [type]       [description]
     */
    public function baceSettlement($bet, $back, $uid)
    {
        $lose = $back - $bet;
        $ugModel = new UserGradeModel;
        $uModel = new UserModel;
        $innings = $this->table->getTableCurInnings();

        $listData = [];
        $tmp = [
            'tid' => $this->tid,
            'uid' => $uid,
            'innings' => $innings,
            'win' => $lose
        ];
        array_push($listData, $tmp);
        $ugModel->insertGrade($listData);
        $uModel->incBalance($uid, $back);
    }

    /**
     * 赢结算
     *
     * @return [type] [description]
     */
    public function winSettlement($bet, $win, $uid)
    {
        $ugModel = new UserGradeModel;
        $uModel = new UserModel;
        $innings = $this->table->getTableCurInnings();

        $listData = [];
        $tmp = [
            'tid' => $this->tid,
            'uid' => $uid,
            'innings' => $innings,
            'win' => $win - $bet
        ];
        array_push($listData, $tmp);
        $ugModel->insertGrade($listData);
        $uModel->incBalance($uid, $win);
    }

    /**
     * 按牌面大到小排序
     *
     * @param  [type] $cards [description]
     * @return [type]        [description]
     */
    public function sizeToSmallCardsSort($cards)
    {
        $newPoker = new NewPoker;
        foreach ($cards as $k1 => $v1) {
            $k1Uid = $this->table->getUidByPos($k1);
            $k1Hands = $this->getHandsByUid($k1Uid);
            $k1Matrix = $newPoker->getCardsMatrix($k1Hands);
            $k1TypeAndNumber = $newPoker->getCardTypeAndNumber($k1Matrix);

            foreach ($cards as $k2 => $v2) {
                if ($k1 >= $k2) continue;
                $k2Uid = $this->table->getUidByPos($k2);
                $k2Hands = $this->getHandsByUid($k2Uid);
                $k2Matrix = $newPoker->getCardsMatrix($k2Hands);
                $k2TypeAndNumber = $newPoker->getCardTypeAndNumber($k2Matrix);

                if ($k1TypeAndNumber['type'] < $k2TypeAndNumber['type']) {
                    $temp = $cards[$k1];
                    $cards[$k1] = $cards[$k2];
                    $cards[$k2] = $temp;
                } elseif ($k1TypeAndNumber['type'] === $k2TypeAndNumber['type'] &&
                    $k1TypeAndNumber['number'] < $k2TypeAndNumber['number']) {
                    $temp = $cards[$k1];
                    $cards[$k1] = $cards[$k2];
                    $cards[$k2] = $temp;
                }

            }
        }
        return $cards;
    }

    /**
     * 判定结算
     *
     * @return boolean [description]
     */
    public function isSettlement()
    {
        $flows = $this->getSpeakFlows();

        // 判断弃牌数
        $disCardCount = 0;
        foreach ($flows as $k => $v) {
            if ($v['isDisCard']) {
                $disCardCount++;
            }
        }
        if ($disCardCount >= count($flows) - 1) {
            return true;
        }

        // 判断发牌说话完成结算
        if ($this->getHandCardsCount() >= 5) {
            $speakCount = 0;
            foreach ($flows as $k => $v) {
                if ($v['speak']) {
                    $speakCount++;
                }
            }

            $notDisCardCount = count($flows) - $disCardCount;
            if ($speakCount == $notDisCardCount) {
                return true;
            }
        }

        return false;
    }

    /**
     * 根据用户id打开手牌
     *
     * @param  [type] $uid [description]
     * @return [type]      [description]
     */
    public function openCardByUid($uid)
    {
        if ($this->isDisCard($uid)) {
            return false;
        }
        $myCards  = $this->getHandsByUid($uid);
        $key = $this->gkey['hands'].$uid;
        $myCards[0]['status'] = true;
        RedisUtils::set($key, $myCards);
        return true;
    }

    /**
     * 获取玩家手牌统计
     *
     * @return [type] [description]
     */
    public function getHandCardsCount()
    {
        $flows = $this->getSpeakFlows();

        foreach ($flows as $k => $v) {
            if (!$v['isDisCard']) {
                $uid   = $this->table->getUidByPos($k);
                $hands = $this->getHandsByUid($uid);
                return count($hands);
            }
        }
        return 0;
    }

    /**
     * 获取下一家未弃牌位置
     *
     * @return [type] [description]
     */
    public function getNextNotDisCardPos()
    {
        $flows  = $this->getSpeakFlows();
        $curPos = (int)$this->getSpeakPos();

        $notDisCardList = [];
        foreach ($flows as $k => $v) {
            if (!$v['isDisCard']) {
                $notDisCardList[$k] = $v;
            }
        }

        $nextPos = 0;
        $count = 0;
        for ($i=$curPos; $i<5; $i++) {
            if (!empty($notDisCardList[$i])) {
                $nextPos = $i;
                $count++;
                if ($count >= 2) break;
            }
        }
        if ($curPos == $nextPos) {
            $nextPos = key($notDisCardList);
        }

        $this->setSpeakPos($nextPos);
        return $nextPos;
    }

    /**
     * 梭哈
     *
     * @return boolean [description]
     */
    public function isAllIn($pos)
    {
        $flows = $this->getSpeakFlows();

        if (empty($flows[$pos]['allin'])) {
            return false;
        }

        return $flows[$pos]['allin'];
    }

    /**
     * 判断是否全部说话
     *
     * @return boolean [description]
     */
    public function isAllSpeak()
    {
        $flows = $this->getSpeakFlows();

        foreach ($flows as $k => $v) {
            if (!$v['speak'] && !$v['isDisCard']) {
                return false;
            }
        }

        return true;
    }

    /**
     * 发手牌
     *
     * @return [type] [description]
     */
    public function sendHands()
    {
        $pos = $this->table->getTablePos();

        $newPoker = new NewPoker();
        $cards = $newPoker->doShuffle();

        // 发手牌
        for ($i = 0; $i < 2; $i++) {
            foreach ($pos as $k => $v) {
                $cardArray = $newPoker->sendCard($cards);
                $this->setHandsByUid($v, $cardArray['card'], $i);
                $cards = $cardArray['cards'];
            }
        }

        // 保存牌堆
        RedisUtils::set($this->gkey['cards'], $cards);
        return true;
    }

    /**

    /**
     * 发牌
     *
     * @return [type] [description]
     */
    public function sendCard()
    {
        // 判断手牌的数目
        $cardsCount = $this->getHandCardsCount();

        if ($cardsCount >= 5) {
            return false;
        }

        // 判断是否全部说话
        if (!$this->isAllSpeak()) {
            return false;
        }

        $cards = $this->getCards();
        $pos = $this->table->getTablePos();
        $newPoker = new NewPoker();
        foreach ($pos as $k => $v) {
            if (!$this->isDisCard($v)) {
                $cardArray = $newPoker->sendCard($cards);
                $this->setHandsByUid($v, $cardArray['card'], $cardsCount);
                $cards = $cardArray['cards'];
                if (!$this->isAllIn($k)) {
                    $this->resetSpaek($k); // 重置说话状态
                }
            }
        }

        // 重置状态
        $this->setMultiple(0);
        $this->setBet(0);

        // 保存牌堆
        RedisUtils::set($this->gkey['cards'], $cards);
        return true;
    }

    /**
     * 创建叫牌的流程
     *
     * @return [type] [description]
     */
    public function buildSpeakFlow()
    {
        $key = $this->gkey['flow'];
        $hands = $this->getTablePosHands();
        $flows = $this->getSpeakFlows();

        $newPoker = new NewPoker();
        $max = [
            'count' => 0,
            'pos'   => 0
        ];
        foreach ($hands as $k1 => $v1) {
            $lastMatrix = $newPoker->getLastCardMatrix($v1);
            $lastCount  = $newPoker->getCardCountByCardsMatrix($lastMatrix);
            if ($lastCount > $max['count']) {
                $max['count'] = $lastCount;
                $max['pos'] = $k1;
            }

            $flows[$k1]['speak'] = false;
            $flows[$k1]['isDisCard'] = empty($flows[$k1]['isDisCard'])
                ? false : $flows[$k1]['isDisCard'];
            $flows[$k1]['allin'] = empty($flows[$k1]['allin'])
                ? false : $flows[$k1]['allin'];
            $flows[$k1]['fill'] = array(1, 1, 1, 1);
            $flows[$k1]['oper'] = array(0, 1, 0, 0, 0, 0);
        }
        $flows[$max['pos']]['oper'] = array(1, 1, 1, 0, 1, 1);
        $flows[$max['pos']]['fill'] = array(1, 1, 1, 1);

        // ======================================= //
        // array 根据pos位置发牌
        //   0 => array (
        //      speak: false,  // 说话
        //      oper: array(0, 0, 0, 0, 0, 0), // 操作按钮过、看、弃、跟、加、梭哈
        //      isDisCard: false, // 弃牌
        //      allin: false,  // 梭哈
        //      fill: array(0, 0, 0, 0) // 加注 x2 x3 x5 x10
        //  )...
        // ======================================= //

        RedisUtils::set($this->gkey['speakpos'], $max['pos']);
        RedisUtils::set($key, $flows);
        return true;
    }

    /**
     * 获取说话的流程
     *
     * @return [type] [description]
     */
    public function getSpeakFlows()
    {
        $key  = $this->gkey['flow'];
        $flow = RedisUtils::get($key);
        if (empty($flow)) {
            return [];
        }
        ksort($flow);
        return $flow;
    }

    /**
     * 设置说话流程
     *
     * @param [type] $pos 自己的位置
     */
    public function setSpeakFlows($pos, $isDisCard = false, $allin = false)
    {
        $flows  = $this->getSpeakFlows();
        $nextPos = $this->getSpeakPos();

        if (!$isDisCard) {
            $isDisCard = $flows[$pos]['isDisCard'];
        }
        if (!$allin) {
            $allin = $flows[$pos]['allin'];
        }

        $flows[$pos] = [
            'speak' => true,
            'isDisCard' => $isDisCard,
            'allin' => $allin,
            'fill'  => array(0, 0, 0, 0),
            'oper'  => array(0, 1, 0, 0, 0, 0)
        ];
        $flows[$nextPos] = [
            'speak' => $flows[$nextPos]['speak'],
            'isDisCard' => $flows[$nextPos]['isDisCard'],
            'allin' => $flows[$nextPos]['allin'],
            'fill'  => $this->getFill(),
            'oper'  => $this->getOper()
        ];

        RedisUtils::set($this->gkey['flow'], $flows);
        return true;
    }

    /**
     * 重置说话方式
     *
     * @param  [type] $pos [description]
     * @return [type]      [description]
     */
    public function resetSpaek($pos)
    {
        $flows = $this->getSpeakFlows();
        $flows[$pos]['speak'] = false;
        RedisUtils::set($this->gkey['flow'], $flows);
        return true;
    }

    /**
     * 全部重置说话
     *
     * @return [type] [description]
     */
    public function allResetSpeak()
    {
        $flows = $this->getSpeakFlows();
        if (empty($flows)) $flows = [];

        foreach ($flows as $k => &$v) {
            if (!$v['isDisCard'] && !$v['allin']) {
                $v['speak'] = false;
            }
        }
        RedisUtils::set($this->gkey['flow'], $flows);
        return true;
    }

    /**
     * 获取当前说话的位置
     *
     * @return [type] [description]
     */
    public function getSpeakPos()
    {
        $pos = RedisUtils::get($this->gkey['speakpos']);

        if (empty($pos)) {
            return 0;
        }
        return $pos;
    }

    /**
     * 设置当前说话位置
     *
     * @param [type] $pos [description]
     */
    public function setSpeakPos($pos)
    {
        RedisUtils::set($this->gkey['speakpos'], $pos);
        return $pos;
    }

    /**
     * 获取自己的操作方式
     *
     * @return [type]      [description]
     */
    public function getOperByUid()
    {
        $pos = $this->table->getPlayerPos($this->uid);
        $flows = $this->getSpeakFlows();

        if (empty($flows[$pos]['oper'])) {
            $oper = array(0, 1, 0, 0, 0, 0);
        } else {
            $oper = $flows[$pos]['oper'];
        }

        return $oper;
    }

    /**
     * 获取自己的加注方式
     *
     * @return [type] [description]
     */
    public function getFillByUid()
    {
        $pos = $this->table->getPlayerPos($this->uid);
        $flows = $this->getSpeakFlows();

        if (empty($flows[$pos]['fill'])) {
            $fill = array(0, 0, 0, 0);
        } else {
            $fill = $flows[$pos]['fill'];
        }

        return $fill;
    }

    /**
     * 获取操作
     * 过牌，看牌，弃牌，跟注，加注，梭哈
     *
     * @return [type] [description]
     */
    public function getOper()
    {
        $oper = RedisUtils::get($this->gkey['oper']);
        if (empty($oper)) {
            return array(1, 1, 1, 0, 1, 1);
        }
        return $oper;
    }

    /**
     * 设置下家操作
     *
     * @param [type] $array [description]
     */
    public function setOper($array)
    {
        RedisUtils::set($this->gkey['oper'], $array);
    }

    /**
     * 获取加注
     * x2 x3 x5 x10
     *
     * @return [type] [description]
     */
    public function getFill()
    {
        $fill = RedisUtils::get($this->gkey['fill']);
        if (empty($fill)) {
            return array(1, 1, 1, 1);
        }
        return $fill;
    }

    /**
     * 设置加注操作
     * @param [type] $array [description]
     */
    public function setFill($array)
    {
        RedisUtils::set($this->gkey['fill'], $array);
    }

    /**
     * 获取当前的投注金额
     *
     * @return [type] [description]
     */
    public function getBet()
    {
        $bet = RedisUtils::get($this->gkey['bet']);
        if (empty($bet)) {
            return 0;
        }
        return $bet;
    }

    /**
     * 设置当前投注金额
     *
     * @param [type] $bet [description]
     */
    public function setBet($bet)
    {
        RedisUtils::set($this->gkey['bet'], $bet);
    }

    /**
     * 获取当前投注的倍数
     *
     * @return [type] [description]
     */
    public function getMultiple()
    {
        $multiple = RedisUtils::get($this->gkey['multiple']);
        if (empty($multiple)) {
            return 0;
        }
        return $multiple;
    }

    /**
     * 设置投注的倍数
     *
     * @param [type] $multiple [description]
     */
    public function setMultiple($multiple)
    {
        RedisUtils::set($this->gkey['multiple'], $multiple);
    }

    /**
     * 获取牌桌上所有用户的手牌
     *
     * @return [type] [description]
     */
    public function getTablePosHands()
    {
        $pos = $this->table->getTablePos();
        $allHands = [];

        foreach ($pos as $k => $v) {
            $allHands[$k] = $this->getHandsByUid($v);
        }
        return $allHands;
    }

    /**
     * 设置用户手牌
     *
     * @param [type] $uid  [description]
     * @param [type] $card [description]
     * @param [type] $pos  牌位置 0-第一张牌 1-第二 2-第三 3-第四 4-第五
     */
    public function setHandsByUid($uid, $card, $pos)
    {
        $key = $this->gkey['hands'].$uid;
        $hands = $this->getHandsByUid($uid);

        $status = true;
        if ($pos == 0) {
            $status = false;
        }

        $hands[$pos] = [
            'card' => $card,
            'status' => $status
        ];
        RedisUtils::set($key, $hands);
        return true;
    }

    /**
     * 根据用户id获取手牌
     *
     * @param [type] $uid [description]
     * @return [type] [description]
     */
    public function getHandsByUid($uid)
    {
        $key = $this->gkey['hands'].$uid;
        $hands = RedisUtils::get($key);

        if (empty($hands)) {
            return [];
        }

        // 二维数组 [['card' => 牌点数, 'status' => 牌状态]]
        return $hands;
    }

    /**
     * 获取牌堆
     *
     * @return [type] [description]
     */
    public function getCards()
    {
        $key = $this->gkey['cards'];
        $cards = RedisUtils::get($key);
        if (empty($cards)) {
            $newPoker = new NewPoker();
            $cards = $newPoker->doShuffle();
        }

        return $cards;
    }

    /**
     * 判断弃牌
     *
     * @param  [type]  $uid [description]
     * @return boolean      [description]
     */
    public function isDisCard($uid)
    {
        $hands = $this->getHandsByUid($uid);

        if (empty($hands)) {
            return false;
        }

        if ($hands[1]['status']) {
            return false;
        }

        return true;
    }

    /**
     * 游戏准备
     *
     * @return [type] [description]
     */
    public function ready()
    {
        $key = $this->table->key['userinfo'].$this->uid;
        $uinfo = RedisUtils::get($key);

        if (empty($uinfo)) {
            return false;
        }

        if ($uinfo['status'] == 2) {
            return false;
        }

        $uinfo['status'] = $uinfo['status'] == 1 ? 0 : 1;
        RedisUtils::set($key, $uinfo);
        return true;
    }

    /**
     * 扣除玩家低注
     *
     * @return [type] [description]
     */
    public function buckleLowNote()
    {
        $pos = $this->table->getTablePos();
        $user = $this->table->getTableAllUserBaseInfo($pos);
        $config = $this->table->config;

        $uModel = new UserModel;
        $uModel->buckleLowNote($pos, $config['low_grade']);
        foreach ($user as $k => $v) {
            if (!empty($v['uid'])) {
                $this->table->setUserinfo($v['uid'], $config['low_grade']);
            }
        }
    }

    /**
     * 游戏开始
     *
     * @return [type] [description]
     */
    public function start()
    {
        $key = $this->table->key['userinfo'].$this->uid;
        $gkey = $this->table->key['status'];
        $ikey = $this->table->key['innings'];
        $pos = $this->table->getTablePos();

        $uinfo = RedisUtils::get($key);

        if ($this->table->getTotalNumber() < 2) {
            $this->table->result['msg'] = '人数不足无法开始';
            return $this->table->result;
        }

        $userlist = $this->table->getTableAllUserBaseInfo($pos);
        foreach ($userlist as $k => $v) {
            if ($v['status'] == 0) {
                $this->table->result['msg'] = '所有玩家准备才可开始游戏';
                return $this->table->result;
            }
        }

        if ($uinfo['status'] != 2) {
            $this->table->result['msg'] = '您不是房主，无法开始游戏';
            return $this->table->result;
        }

        $status = RedisUtils::get($gkey);
        if ($status) {
            $this->table->result['msg'] = '游戏已经开始';
            return $this->table->result;
        }

        $innings = $this->table->getTableCurInnings() + 1;
        if ($innings > $this->table->config['innings']) {
            $this->table->result['msg'] = '牌桌已解散';
            return $this->table->result;
        }

        RedisUtils::set($ikey, $innings);
        RedisUtils::set($gkey, true);
        return [
            'code' => 200,
            'msg'  => '开始游戏'
        ];
    }

    /**
     * 重新开始游戏(继续游戏)
     *
     * @return [type] [description]
     */
    public function restart()
    {
        $gkey = $this->table->key['status'];
        $pos = $this->table->getTablePos();

        $status = RedisUtils::get($gkey);
        if (!$status) {
            return false;
        }

        foreach ($this->gkey as $k => $v) {
            RedisUtils::rm($v);
        }

        foreach ($pos as $k => $v) {
            foreach ($this->gkey as $k1 => $v1) {
                RedisUtils::rm($v1.$v);
            }
        }

        RedisUtils::set($gkey, false);
        $userlist = $this->table->getTableAllUserBaseInfo($pos);
        $newUserList = [];
        foreach ($userlist as $k => $v) {
            if (!empty($v)) {
                $tmp = (new UserModel)->getBaseInfoByUid($v['uid']);
                $newUserList[$k] = [
                    'name'  => $tmp['username'],
                    'uid'   => $tmp['uid'],
                    'type'  => $tmp['type'], // 0-普通 1-透视 2-好牌 3-全部
                    'balance' => $tmp['balance'],
                    'bet'     => 0,
                    'status'  => $this->table->isMaster($v['uid']) ? 2 : 0, // 0-未准备 1-已准备 2-房主
                ];
                RedisUtils::set($this->table->key['userinfo'].$v['uid'], $newUserList[$k]);
            }
        }
        return $newUserList;
    }

    /**
     * 判断是否需要自动过牌
     * 自动过牌的条件:
     *   1.最多只剩余一家未梭哈
     *   2.如果剩余一家未梭哈则判断下注的金额需要大于等于最大的梭哈下注牌面
     *
     * @return boolean [description]
     */
    public function isNeedAutoACard()
    {
        $flows = $this->getSpeakFlows();
        $pos = $this->table->getTablePos();
        $users = $this->table->getTableAllUserBaseInfo($pos);
        $numbers = $this->table->getTotalNumber();

        $allinCount = 0; $disCardCount = 0;
        $maxBetPos = -1; $maxBetAmount = 0;
        foreach ($flows as $k => $v) {
            if ($v['allin']) {
                $allinCount++;
            }
            if ($v['isDisCard']) {
                $disCardCount++;
            }
        }
        if (!empty($flows)) {
            foreach ($users as $k => $v) {
                if ($v['bet'] > $maxBetAmount) {
                    $maxBetAmount = $v['bet'];
                    $maxBetPos = $k;
                } elseif ($v['bet'] == $maxBetAmount &&
                    !$flows[$k]['allin'] &&
                    !$flows[$k]['isDisCard']) {
                    $maxBetAmount = $v['bet'];
                    $maxBetPos = $k;
                }
            }

            if ($allinCount >= $numbers &&
                !$this->isSettlement()) {
                return true;
            }
            if ($allinCount == $numbers-1 &&
                $maxBetPos != -1 &&
                !$flows[$maxBetPos]['allin'] &&
                !$flows[$maxBetPos]['isDisCard'] &&
                !$this->isSettlement()) {
                return true;
            }
        }

        return false;
    }

    /**
     * 已经梭哈自动过牌
     *
     * @return [type] [description]
     */
    public function allinAutoACard()
    {
        // 获取位置
        $nextPos = $this->getNextNotDisCardPos();
        $pos = $this->table->getPlayerPos($this->uid);

        if (!$this->isAllIn($pos) && !$this->isNeedAutoACard()) {
            return [
                'code' => 0,
                'msg'  => '不可自动过牌'
            ];
        }

        // 判断下家是否已经梭哈
        if ($this->isAllIn($nextPos)) {
            $oper = array(0, 1, 0, 0, 0, 0);
            $this->setOper($oper);
            $this->setSpeakFlows($pos);
            $success = [
                'msg' => '下家已经梭哈',
                'code' => 2001, // 下家梭哈
                'uid'  => $this->table->getUidByPos($nextPos)
            ];
        } else {
            $this->setOper($this->getOper());
            $this->setFill($this->getFill());
            $this->setSpeakFlows($pos);
            $success = [
                'msg' => '自动过牌成功',
                'code' => 200
            ];
        }

        // 判断是否需要发牌
        if ($this->isAllSpeak()) {
            $success = [
                'msg' => '全部说话发牌',
                'code' => 2002,
                'uid'  => $this->table->getUidByPos($nextPos)
            ];
        }

        // 判断是否需要结算
        if ($this->isSettlement()) {
            $success = [
                'msg'  => '结算',
                'code' => 2003,
                'uid'  => $this->table->getUidByPos($nextPos)
            ];
        }

        return $success;
    }

    /**
     * 过牌
     *
     * @return [type] [description]
     */
    public function aCard()
    {
        $result = $this->table->result;

        // 判断自己是否能过牌
        if (!$this->getOperByUid()[0]) {
            $result['msg'] = '您当前不能过牌';
            return $result;
        }

        // 获取位置
        $nextPos = $this->getNextNotDisCardPos();
        $pos = $this->table->getPlayerPos($this->uid);

        // 判断下家是否已经梭哈
        if ($this->isAllIn($nextPos)) {
            $oper = array(0, 1, 0, 0, 0, 0);
            $this->setOper($oper);
            $this->setSpeakFlows($pos);
            $success = [
                'msg' => '下家已经梭哈',
                'code' => 2001, // 下家梭哈
                'uid'  => $this->table->getUidByPos($nextPos)
            ];
        } else {
            if ($this->isNeedAutoACard()) {
                $this->setOper(array(0, 1, 0, 0, 0, 0));
                $this->setFill(array(1, 1, 1, 1));
            } else {
                $this->setOper(array(1, 1, 1, 0, 1, 1));
                $this->setFill(array(1, 1, 1, 1));
            }
            $this->setSpeakFlows($pos);
            $success = [
                'msg' => '过牌成功',
                'code' => 200
            ];
        }

        // 判断是否需要发牌
        if ($this->isAllSpeak()) {
            $success = [
                'msg' => '全部说话发牌',
                'code' => 2002,
                'uid'  => $this->table->getUidByPos($nextPos)
            ];
        }

        // 判断是否需要结算
        if ($this->isSettlement()) {
            $success = [
                'msg'  => '结算',
                'code' => 2003,
                'uid'  => $this->table->getUidByPos($nextPos)
            ];
        }

        return $success;
    }

    /**
     * 弃牌
     *
     * @return [type] [description]
     */
    public function disCard()
    {
        $result = $this->table->result;

        // 判断是否到自己操作
        if (!$this->getOperByUid()[2]) {
            $result['msg'] = '还未到您操作';
            return $result;
        }

        // 获取位置
        $nextPos = $this->getNextNotDisCardPos();
        $pos = $this->table->getPlayerPos($this->uid);

        // 判断下家是否已经梭哈
        if ($this->isAllIn($nextPos)) {
            $oper = array(0, 1, 0, 0, 0, 0);
            $this->setOper($oper);
            $this->setSpeakFlows($pos, true);
            $success = [
                'msg' => '下家已经梭哈',
                'code' => 2001, // 下家梭哈
                'uid'  => $this->table->getUidByPos($nextPos)
            ];
        } else {
            $this->setOper($this->getOper());
            $this->setFill($this->getFill());
            $this->setSpeakFlows($pos, true);
            $success = [
                'msg' => '过牌成功',
                'code' => 200
            ];
        }

        // 判断是否需要发牌
        if ($this->isAllSpeak()) {
            $success = [
                'msg' => '全部说话发牌',
                'code' => 2002,
                'uid'  => $this->table->getUidByPos($nextPos)
            ];
        }

        // 判断是否需要结算
        if ($this->isSettlement()) {
            $success = [
                'msg'  => '结算',
                'code' => 2003,
                'uid'  => $this->table->getUidByPos($nextPos)
            ];
        }

        $myCards  = $this->getHandsByUid($this->uid);
        $key = $this->gkey['hands'].$this->uid;
        foreach ($myCards as $k => &$v) {
            $v['status'] = false;
        }
        RedisUtils::set($key, $myCards);
        return $success;
    }

    /**
     * 跟牌
     *
     * @return [type] [description]
     */
    public function withCard()
    {
        $result = $this->table->result;
        $uModel = new UserModel;

        // 判断是否到自己操作
        if (!$this->getOperByUid()[3]) {
            $result['msg'] = '还未到您操作';
            return $result;
        }

        // 获取自己需要更注的金额
        $bet = $this->getBet();
        $multiple = $this->getMultiple();
        $uinfo = $this->table->getUserinfo($this->uid);
        $needBet = $bet - $uinfo['bet'];

        // 判断余额是否充足
        if (!$uModel->isBalanceEnough($this->uid, $needBet)) {
            $result['msg'] = "跟注失败余额不足";
            return $result;
        }
        $uModel->incBalance($this->uid, 0-$needBet); // 更新用户余额

        $this->table->setUserinfo($this->uid, $needBet); // 设置用户信息
        $this->setBet($uinfo['bet']); // 设置当前的投注金额

        // 获取位置
        $nextPos = $this->getNextNotDisCardPos();
        $pos = $this->table->getPlayerPos($this->uid);
        $nextUid = $this->table->getUidByPos($nextPos);
        $nextUInfo = $this->table->getUserinfo($nextUid);

        // 获取低注
        $lowNote = (int)$this->table->config['low_grade'];

        // 判断下家是否已经梭哈
        if ($this->isAllIn($nextPos)) {
            $oper = array(0, 1, 0, 0, 0, 0);
            $this->setOper($oper);
            $this->setSpeakFlows($pos);
            $success = [
                'msg' => '下家已经梭哈',
                'code' => 2001, // 下家梭哈
                'uid'  => $this->table->getUidByPos($nextPos)
            ];
        } else {
            // 设置操作-判断下家是否有足够的金额跟注或加注
            // $uinfo['bet'] - $nextUInfo['bet'] $neet
            $nextNeedBet = $uinfo['bet'] - $nextUInfo['bet']; // 下家所需跟注金额

            if ($nextNeedBet > 0) {
                $this->resetSpaek($nextPos); // 重新说话
            }

            if ($nextNeedBet > $nextUInfo['balance']) {
                $oper = array(0, 1, 1, 0, 0, 1);
                $fill = array(0, 0, 0, 0);
            } else {
                if ($multiple === 2) {
                    if ($nextUInfo['balance'] >= $lowNote*10) {
                        $oper = array(0, 1, 1, 1, 1, 1);
                        $fill = array(0, 1, 1, 1);
                    } elseif ($nextUInfo['balance'] >= $lowNote*5 &&
                        $nextUInfo['balance'] < $lowNote*10) {
                        $oper = array(0, 1, 1, 1, 1, 1);
                        $fill = array(0, 1, 1, 0);
                    } elseif ($nextUInfo['balance'] >= $lowNote*3 &&
                        $nextUInfo['balance'] < $lowNote*5) {
                        $oper = array(0, 1, 1, 1, 1, 1);
                        $fill = array(0, 1, 0, 0);
                    } else {
                        $oper = array(0, 1, 1, 1, 0, 1);
                        $fill = array(0, 0, 0, 0);
                    }
                } elseif ($multiple === 3) {
                    if ($nextUInfo['balance'] >= $lowNote*10) {
                        $oper = array(0, 1, 1, 1, 1, 1);
                        $fill = array(0, 0, 1, 1);
                    } elseif ($nextUInfo['balance'] >= $lowNote*5 &&
                        $nextUInfo['balance'] < $lowNote*10) {
                        $oper = array(0, 1, 1, 1, 1, 1);
                        $fill = array(0, 0, 1, 0);
                    } else {
                        $oper = array(0, 1, 1, 1, 0, 1);
                        $fill = array(0, 0, 0, 0);
                    }
                } elseif ($multiple === 5) {
                    if ($nextUInfo['balance'] >= $lowNote*10) {
                        $oper = array(0, 1, 1, 1, 1, 1);
                        $fill = array(0, 0, 0, 1);
                    } else {
                        $oper = array(0, 1, 1, 1, 0, 1);
                        $fill = array(0, 0, 0, 0);
                    }
                } elseif ($multiple === 10) {
                    $oper = array(0, 1, 1, 1, 0, 1);
                    $fill = array(0, 0, 0, 0);
                } else {
                    $oper = array(0, 1, 1, 1, 1, 1);
                    $fill = array(1, 1, 1, 1);
                }
            }

            $this->setOper($oper);
            $this->setFill($fill);
            $this->setSpeakFlows($pos);
            $success = [
                'msg' => '跟牌成功',
                'code' => 200
            ];
        }

        // 判断是否需要发牌
        if ($this->isAllSpeak()) {
            $success = [
                'msg' => '全部说话发牌',
                'code' => 2002,
                'uid'  => $this->table->getUidByPos($nextPos)
            ];
        }

        // 判断是否需要结算
        if ($this->isSettlement()) {
            $success = [
                'msg'  => '结算',
                'code' => 2003,
                'uid'  => $this->table->getUidByPos($nextPos)
            ];
        }

        return $success;
    }

    /**
     * 加注
     *
     * @return [type] [description]
     */
    public function fillingCard($multiple)
    {
        $result = $this->table->result;
        $uModel = new UserModel;

        // 判断是否到自己操作
        if (!$this->getOperByUid()[4]) {
            $result['msg'] = '还未到您操作';
            return $result;
        }

        // 判断是否可以操作的倍数
        $fill = $this->getFillByUid();
        if ($multiple === 2 && !$fill[0] ||
            $multiple === 3 && !$fill[1] ||
            $multiple === 5 && !$fill[2] ||
            $multiple === 10 && !$fill[3]) {
            $result['msg'] = "加注失败,不能加{$multiple}倍数";
            return $result;
        }

        // 获取低注
        $lowNote = (int)$this->table->config['low_grade'];

        // 判断余额是否充足
        if (!$uModel->isBalanceEnough($this->uid, $lowNote*$multiple)) {
            $result['msg'] = "加注失败余额不足";
            return $result;
        }
        $uModel->incBalance($this->uid, 0-$lowNote*$multiple);

        // 设置用户信息
        $this->table->setUserinfo($this->uid, $lowNote*$multiple);
        $uinfo = $this->table->getUserinfo($this->uid);

        // 设置当前的投注金额
        $this->setBet($uinfo['bet']);
        $this->setMultiple($multiple);

        // 获取位置
        $nextPos = $this->getNextNotDisCardPos();
        $pos = $this->table->getPlayerPos($this->uid);
        $nextUid = $this->table->getUidByPos($nextPos);
        $nextUInfo = $this->table->getUserinfo($nextUid);

        // 判断下家是否已经梭哈
        if ($this->isAllIn($nextPos)) {
            $oper = array(0, 1, 0, 0, 0, 0);
            $this->setOper($oper);
            $this->setSpeakFlows($pos);
            $success = [
                'msg' => '下家已经梭哈',
                'code' => 2001, // 下家梭哈
                'uid'  => $this->table->getUidByPos($nextPos)
            ];
        } else {
            // 设置操作-判断下家是否有足够的金额跟注或加注
            // $uinfo['bet'] - $nextUInfo['bet'] $neet
            $needBet = $uinfo['bet'] - $nextUInfo['bet']; // 下家所需跟注金额
            $this->allResetSpeak();

            if ($needBet > $nextUInfo['balance']) {
                $oper = array(0, 1, 1, 0, 0, 1);
                $fill = array(0, 0, 0, 0);
            } else {
                if ($multiple === 2) {
                    if ($nextUInfo['balance'] >= $lowNote*10) {
                        $oper = array(0, 1, 1, 1, 1, 1);
                        $fill = array(0, 1, 1, 1);
                    } elseif ($nextUInfo['balance'] >= $lowNote*5 &&
                        $nextUInfo['balance'] < $lowNote*10) {
                        $oper = array(0, 1, 1, 1, 1, 1);
                        $fill = array(0, 1, 1, 0);
                    } elseif ($nextUInfo['balance'] >= $lowNote*3 &&
                        $nextUInfo['balance'] < $lowNote*5) {
                        $oper = array(0, 1, 1, 1, 1, 1);
                        $fill = array(0, 1, 0, 0);
                    } else {
                        $oper = array(0, 1, 1, 1, 0, 1);
                        $fill = array(0, 0, 0, 0);
                    }
                } elseif ($multiple === 3) {
                    if ($nextUInfo['balance'] >= $lowNote*10) {
                        $oper = array(0, 1, 1, 1, 1, 1);
                        $fill = array(0, 0, 1, 1);
                    } elseif ($nextUInfo['balance'] >= $lowNote*5 &&
                        $nextUInfo['balance'] < $lowNote*10) {
                        $oper = array(0, 1, 1, 1, 1, 1);
                        $fill = array(0, 0, 1, 0);
                    } else {
                        $oper = array(0, 1, 1, 1, 0, 1);
                        $fill = array(0, 0, 0, 0);
                    }
                } elseif ($multiple === 5) {
                    if ($nextUInfo['balance'] >= $lowNote*10) {
                        $oper = array(0, 1, 1, 1, 1, 1);
                        $fill = array(0, 0, 0, 1);
                    } else {
                        $oper = array(0, 1, 1, 1, 0, 1);
                        $fill = array(0, 0, 0, 0);
                    }
                } elseif ($multiple === 10) {
                    $oper = array(0, 1, 1, 1, 0, 1);
                    $fill = array(0, 0, 0, 0);
                } else {
                    $oper = array(0, 1, 1, 1, 1, 1);
                    $fill = array(1, 1, 1, 1);
                }
            }

            $this->setOper($oper);
            $this->setFill($fill);
            $this->setSpeakFlows($pos);
            $success = [
                'msg' => '加注成功',
                'code' => 200
            ];
        }

        return $success;
    }

    /**
     * 梭哈
     *
     * @return [type] [description]
     */
    public function allinCard()
    {
        $result = $this->table->result;
        $uModel = new UserModel;

        // 判断是否到自己操作
        if (!$this->getOperByUid()[5]) {
            $result['msg'] = '还未到您操作';
            return $result;
        }

        $uinfo = $this->table->getUserinfo($this->uid); // 获取用户信息
        $uModel->incBalance($this->uid, 0-$uinfo['balance']); // 更新用户余额
        $this->table->setUserinfo($this->uid, $uinfo['balance']); // 设置用户信息
        $curinfo = $this->table->getUserinfo($this->uid);
        $this->setBet($curinfo['bet']); // 设置当前的投注金额

        // 获取位置
        $nextPos = $this->getNextNotDisCardPos();
        $pos = $this->table->getPlayerPos($this->uid);
        $nextUid = $this->table->getUidByPos($nextPos);
        $nextUInfo = $this->table->getUserinfo($nextUid);

        // 判断下家是否已经梭哈
        if ($this->isAllIn($nextPos)) {
            $oper = array(0, 1, 0, 0, 0, 0);
            $this->setOper($oper);
            $success = [
                'msg' => '下家已经梭哈',
                'code' => 2001, // 下家梭哈
                'uid'  => $this->table->getUidByPos($nextPos)
            ];
        } else {
            // 设置操作-判断下家是否有足够的金额跟注或加注
            // $uinfo['bet'] - $nextUInfo['bet'] $neet
            $needBet = $curinfo['bet'] - $nextUInfo['bet']; // 下家所需跟注金额
            $this->resetSpaek($nextPos);

            if ((int)$needBet >= (int)$nextUInfo['balance']) {
                $oper = array(0, 1, 1, 0, 0, 1);
                $fill = array(0, 0, 0, 0);
            } else {
                $oper = array(0, 1, 1, 1, 0, 1);
                $fill = array(0, 0, 0, 0);
            }

            $this->setOper($oper);
            $this->setFill($fill);
            $success = [
                'msg' => '梭哈成功',
                'code' => 200
            ];
        }
        $this->setSpeakFlows($pos, false, true);

        // 判断是否需要发牌
        if ($this->isAllSpeak()) {
            $success = [
                'msg' => '全部说话发牌',
                'code' => 2002,
                'uid'  => $this->table->getUidByPos($nextPos)
            ];
        }

        // 判断是否需要结算
        if ($this->isSettlement()) {
            $success = [
                'msg'  => '结算',
                'code' => 2003,
                'uid'  => $this->table->getUidByPos($nextPos)
            ];
        }

        return $success;
    }
}
